
class Snake {
  // 表示蛇头的元素
  head: HTMLElement;
  // 蛇的身体（包括蛇头）
  bodies: HTMLCollection;
  // 获取蛇的容器
  element: HTMLElement;

  constructor() {
    this.element = document.getElementById('snake')!;
    // 操作蛇里面的div   仅返回匹配指定选择器的第一个元素
    this.head = document.querySelector('#snake > div') as HTMLElement; // 断言
    // this.bodies = document.querySelectorAll('#snake > div'); x NodeListOf 
    this.bodies = this.element.getElementsByTagName('div');
  }

  // 获取蛇的坐标（蛇头坐标）
  get X() {
    return this.head.offsetLeft;
  }
  get Y() {
    return this.head.offsetTop;
  }

  // 设置蛇头的坐标
  set X(value: number) {
    // 如果新值和旧值相同，则直接返回不再修改
    if(this.X === value) return
    // X值的合法范围0-290之间
    if(value < 0 || value > 290) { // 进入判断说明蛇撞墙了
      throw new Error('蛇撞墙了！');
    }

    // 修改X时，是在修改水平坐标，蛇在左右移动，蛇在向左移动时，不能向右掉头，反之亦然；
    if(this.bodies[1] && (this.bodies[1] as HTMLElement).offsetLeft === value) {
      console.log('水平方向发生了掉头')
      // 如果发生了掉头，让蛇向反方向继续移动
      if(value > this.X) {
        // 如果新值value大于旧值X，则说明蛇在向右走，此时发生掉头，应该使蛇继续向左走
        value = this.X - 10
      } else {
        // 向左走
        value = this.X + 10
      }
    }
    
    // 移动身体
    this.moveBody()

    this.head.style.left = value + 'px';
    this.checkHeadBody()
  }
  set Y(value: number) {
    // 如果新值和旧值相同，则直接返回不再修改
    if(this.Y === value) return
    if(value < 0 || value > 290) { // 进入判断说明蛇撞墙了
      throw new Error('蛇撞墙了！')
    }

    if(this.bodies[1] && (this.bodies[1] as HTMLElement).offsetTop === value) {
      console.log('竖直方向发生了掉头')
      if(value > this.Y) {
        value = this.Y - 10
      } else {
        value = this.Y + 10
      }
    }

    // 移动身体
    this.moveBody()

    this.head.style.top = value + 'px';
    this.checkHeadBody()
  }

  // 蛇增加身体的方法
  addBody() {
    // 向element中添加一个div
    const creatDiv: HTMLElement = document.createElement('div');
    this.element.insertAdjacentElement("beforeend", creatDiv)
    // element.insertAdjacentElement(position, element);
    // 'beforebegin': 在该元素本身的前面.
    // 'afterbegin':只在该元素当中, 在该元素第一个子孩子前面.
    // 'beforeend':只在该元素当中, 在该元素最后一个子孩子后面.
    // 'afterend': 在该元素本身的后面.
  }
  // 添加一个蛇身体移动的方法
  moveBody() {
    // 将后边的身体设置为前边身体的位置：举例子：第四节= 第三节的位置；第三节=第二节的位置；第二节=蛇头的位置；
    for(let i = this.bodies.length-1;i>0;i--) { // >0 不动蛇头的位置
      let X = (this.bodies[i-1] as HTMLElement).offsetLeft;
      let Y = (this.bodies[i-1] as HTMLElement).offsetTop;

      // 将值设置到当前身体上
      (this.bodies[i] as HTMLElement).style.left = X + 'px';
      (this.bodies[i] as HTMLElement).style.top = Y + 'px';
    }
  }
  // 检查蛇头是否撞到身体
  checkHeadBody() {
    // 获取所有的身体，检查其是否和蛇头的坐标法神重叠
    for(let i=1;i<this.bodies.length;i++) {
      let bd = this.bodies[i] as HTMLElement
      if(this.X === bd.offsetLeft && this.Y === bd.offsetTop) {
        // 进入判断说明蛇头撞到了身体，游戏结束
        throw new Error('撞到自己了！')
      }
    }

  }
}
// 最后完善：蛇不能反向掉头走；蛇不能撞到自己；
export default Snake;
